-- Coding Format : UTF-8
-- 「agent.lua 脚本介绍」
-- 玩家需要服务，agent就是服务员，一个玩家对应一个agent
-- agent负责处理自己的玩家发来的消息，可以转发或广播消息，也可以做逻辑处理
local skynet = require "skynet"
local socket = require "skynet.socket"
local sprotoloader = require "sprotoloader"

local PlayerInRoom = require "src.db.PlayerInRoom"

local host
local proto_pack
local client_fd

local CMD = {}
local REQUEST = {}

local userService
local roomService
local watchdog

-- 本agent服务的用户信息
local username
local roomId = 0
local isReady = false

local function send_request(pack, fd)
    local package = string.pack(">s2", pack)
    socket.write(fd, package)
end

local function reply(protoName, res)
    send_request(proto_pack(protoName, res), client_fd)
end

local function getChatRecordByUsers(userA, userB)
    print("查询玩家间信息:" .. userA.username .. "和" .. userB.username)
    local records = skynet.call(chatService, "lua", "getRecords", userA:simple(), userB:simple())
    local speakers = {}
    local contents = {}
    for i, record in ipairs(records) do
        speakers[i] = record.sender
        contents[i] = record.content
        print(record.sender .. ":" .. record.content)
    end

    return speakers, contents
end

-- 服务间通信
local function userLogin(username)
    return skynet.call(userService, "lua", "login", {
        username = username,
        fd = client_fd
    })
end

local function userLogout()
    skynet.call(userService, "lua", "logout", {
        username = username
    })
    skynet.call(roomService, "lua", "leaveRoom", {
        username = username
    })
end

local function getRooms()
    return skynet.call(roomService, "lua", "getRooms", {})
end

local function enterRoom(roomId)
    return skynet.call(roomService, "lua", "enterRoom", {
        roomId = roomId,
        username = username
    })
end

local function leaveRoom()
    return skynet.call(roomService, "lua", "leaveRoom", {
        username = username
    })
end

local function ready()
    return skynet.call(roomService, "lua", "ready", {
        username = username
    })
end

local function unready()
    return skynet.call(roomService, "lua", "unready", {
        username = username
    })
end

local function broadcastRoomList()
    return skynet.call(userService, "lua", "broadcastRoomList")
end

local function sendBattleData(data)
    return skynet.call(roomService, "lua", "sendBattleData", {
        sender = username,
        roomId = roomId,
        data = data
    })
end

-- 更新玩家状态
local function updateInfo(info)
    if info == nil then
        return
    else
        roomId = info.roomId
        isReady = info.isReady
    end
end

-- 发送工具函数
local function sendRoomList()
    local rooms = getRooms()
    local info = PlayerInRoom:New(username, roomId, isReady)
    local roomIds, playerNums, playerNumMaxs, isPlayings, usernames, isReadys = {}, {}, {}, {}, {}, {}
    for i, rm in pairs(rooms) do
        roomIds[i] = rm.roomId
        playerNums[i] = rm.playerNum
        playerNumMaxs[i] = rm.playerNumMax
        isPlayings[i] = rm.isPlaying
        for j, player in ipairs(rm.players) do
            local num = #usernames
            usernames[num + 1] = player.username
            isReadys[num + 1] = player.isReady
        end
    end
    reply("roomList", {
        roomId = roomIds,
        playerNum = playerNums,
        playerNumMax = playerNumMaxs,
        isPlaying = isPlayings,
        username = usernames,
        isReady = isReadys,
        yourRoomId = roomId,
        yourReady = isReady
    })
end

local function roomStart()
    print("username:" .. username .. "即将开始游戏")
    reply("roomStart", {})
end

local function battleData(data)
    reply("battleData", data)
end

-- 响应请求
function REQUEST:login()
    username = self.username
    print("收到登录请求，username:" .. username)
    local code
    local msg
    if userLogin(username) then
        -- 登录成功
        code = 0
        msg = "登录成功"
    else
        -- 用户名重复
        code = 1
        msg = "登录失败，用户名重复"
    end
    send_request(proto_pack("login", {
        code = code,
        msg = msg
    }), client_fd)
end

function REQUEST:roomList()
    sendRoomList()
end

function REQUEST:ready()
    updateInfo(ready())
    broadcastRoomList()
end

function REQUEST:unready()
    updateInfo(unready())
    broadcastRoomList()
end

function REQUEST:enterRoom()
    local roomId = self.roomId
    updateInfo(enterRoom(roomId))
    broadcastRoomList()
end

function REQUEST:battleData()
    local round = self.round
    print(username .. " sending battle data, round:" .. round)
    local posx = self.posx
    local posy = self.posy
    local strike = self.strike
    local dodge = self.dodge
    -- 转发给房间里的其他人
    sendBattleData({
        round = round,
        posx = posx,
        posy = posy,
        strike = strike,
        dodge = dodge
    })
end

function REQUEST:gameDone()
    -- 玩家完成游戏，从房间中退出
    leaveRoom()
    broadcastRoomList()
end

-- 基础功能
local function request(name, args, response)
    local f = assert(REQUEST[name])
    local r = f(args)
end

skynet.register_protocol {
    name = "client",
    id = skynet.PTYPE_CLIENT,
    unpack = function(msg, sz)
        return host:dispatch(msg, sz)
    end,
    dispatch = function(fd, _, type, ...)
        skynet.ignoreret() -- session is fd, don't call skynet.ret
        if type == "REQUEST" then
            args = ...

            local ok, result = pcall(request, ...)
            if ok then
                if result then
                    send_request(result, fd)
                end
            else
                skynet.error(result)
            end
        else
            assert(type == "RESPONSE")
            error "This example doesn't support request client"
        end
    end
}

-- 响应其他服务
function CMD.sendRoomList()
    sendRoomList()
end

function CMD.gameStart()
    -- 通知客户端开启游戏
    roomStart()
end

function CMD.battleData(args)
    local data = args.data
    print("receive battleData from:" .. username)
    battleData(data)
end

-- 启动，由watchdog调用
function CMD.start(conf)
    local fd = conf.client
    local gate = conf.gate
    client_fd = fd

    -- slot 1,2 set at main.lua
    host = sprotoloader.load(1):host "package"
    proto_pack = host:attach(sprotoloader.load(2))
    skynet.call(gate, "lua", "forward", fd)
    print("new connect, fd:", fd)
end

-- 断开连接时调用
function CMD.disconnect(fd)
    userLogout()
    leaveRoom()
    broadcastRoomList()
    skynet.exit()
end

skynet.start(function()
    skynet.dispatch("lua", function(_, _, command, ...)
        local f = CMD[command]
        userService = skynet.queryservice("userService")
        roomService = skynet.queryservice("roomService")
        watchdog = skynet.queryservice("watchdog")
        skynet.ret(skynet.pack(f(...)))
    end)
end)
